package ru.nstu.isma.core.lang.semantic;

import org.antlr.v4.runtime.tree.ParseTree;
import ru.nstu.isma.core.hsm.*;
import ru.nstu.isma.core.hsm.exp.EXPPDEOperand;
import ru.nstu.isma.core.hsm.exp.EXPToken;
import ru.nstu.isma.core.hsm.exp.HMExpression;
import ru.nstu.isma.core.hsm.pde.HMPartialDerEquation;
import ru.nstu.isma.core.lang.ast.visitor.*;

import java.util.HashSet;

/**
 * by Alexey Bessonov
 * Date: 28.11.13
 * Time: 23:24
 */
public class ASTTreeTranslator {
    private ParseTree tree;

    private HMStateAutomata automata;

    private HMVariableTable variables;

    public ASTTreeTranslator(ParseTree tree) {
        this.tree = tree;
    }

    public HMModel parse() {


        HMModel model = new HMModel();

        automata = model.getAutomata();
        variables = model.getVariables();

        // 1 Парсим все состояния
        StateVisitor stateNameVisitor = new StateVisitor(automata);
        stateNameVisitor.visit(tree);

        // 2 Парсим все имена переменных
        VariableTableAggregatorVisitor aggregatorVisitor = new VariableTableAggregatorVisitor(variables, automata);
        aggregatorVisitor.visit(tree);

        // 3 постоить для каждого уравения (как глобального так и состояния), константы и начального значения
        // полиз правой части и записать
        PopulateExpressionVisitor expressionVisitor = new PopulateExpressionVisitor(variables, automata);
        expressionVisitor.visit(tree);



        // TODO
        // написать полиз для логики
        // применить на condition
        // парсить from
        // заполнять переходами

        // ОДУ -> ДУЧП
        HashSet<HMEquation> updated = new HashSet<HMEquation>();
        for (String key : variables.keySet()) {
            HMVariable v = variables.get(key);
            if (v instanceof HMDerivativeEquation) {
                HMExpression exp = ((HMDerivativeEquation) v).getRightPart();
                boolean isPartial = false;
                for (EXPToken t : exp.getTokens()) {
                    if (t instanceof EXPPDEOperand) {
                        isPartial = true;
                    }
                }

                if (isPartial) {
                    HMPartialDerEquation partialDerEquation
                            = new HMPartialDerEquation((HMDerivativeEquation) v);
                    updated.add(partialDerEquation);
                }
            }
        }
        HashSet<String> updateCodes = new HashSet<String>();
        for(HMEquation e : updated)  {
            variables.remove(e.getCode());
            variables.add(e);
            updateCodes.add(e.getCode());
        }


        // надо пробежаться по правым частям и заменить на новый объект
//        for (String varKey : variables.keySet()) {
//            HMVariable var = variables.get(varKey);
//            if (var instanceof HMEquation) {
//                HMEquation eq = (HMEquation) var;
//                HMExpression expr = eq.getRightPart();
//                if (expr != null) {
//                    for (EXPToken tt : expr.getTokens()) {
//                        if (tt instanceof EXPOperand && !(tt instanceof EXPPDEOperand)) {
//                            String operandVarCode = ((EXPOperand)tt).getVariable().getCode();
//                            if (updateCodes.contains(operandVarCode)) {
//                                ((EXPOperand) tt).setVariable(variables.get(operandVarCode));
//                            }
//                        } else if (tt instanceof EXPPDEOperand) {
//                            String operandVarCode = ((EXPPDEOperand)tt).getVariable().getCode();
//                            if (updateCodes.contains(operandVarCode)) {
//                                ((EXPPDEOperand) tt).setVariable(variables.get(operandVarCode));
//                            }
//                        }
//                    }
//                }
//            }
//        }

        PopulateStateInfoVisitor stateInfoVisitor = new PopulateStateInfoVisitor(variables, automata);
        stateInfoVisitor.visit(tree);

        // TODO
        // создать сущности ДУЧП
        // заполнить данные аппроксимации

        // смотрим края
        BoundInfoVisitor boundInfoVisitor = new BoundInfoVisitor(variables);
        boundInfoVisitor.visit(tree);

        // 4.1 разобрать выражение (ПОЛИЗ) (ограничение - константы)
        // 4.2 написать небольшой вычислитель для констант
        // 4.3 продумать механизм блокировки зацикливания
        // 4.4 посчитать и записать
        CalcConstVisitor calcConstVisitor = new CalcConstVisitor(variables);
        calcConstVisitor.visit(tree);

        return model;
    }
}
